﻿// <copyright file="FileIconRepository.cs" company="Soumya Chattopadhyay">
// Copyright (c) 2010 All Right Reserved
// </copyright>
// <disclaimer> This software is intented for educational purposes only and not for 
// commerical or prodution use. The software is provided "as is", without warranty 
// of any kind, express or implied, including but not limited to the warranties of 
// merchantability, fitness for a particular purpose and noninfringement. In no event 
// shall the author(s) or copyright holder(s) be liable for any claim, damages or other 
// liability, whether in an action of contract, tort or otherwise, arising from, out of 
// or in connection with the software or the use or other dealings in the software.
// </disclaimer>

namespace My.CodePlex.TFSExplorer
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Windows;
    using System.Windows.Forms;
    using System.Windows.Interop;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Shapes;
    using Microsoft.Win32;

    /// <summary>
    /// The file icon repository
    /// </summary>
    public static class FileIconRepository
    {
        /// <summary>
        /// The cache of file icons
        /// </summary>
        private static Dictionary<string, Icon> iconCache = new Dictionary<string, Icon>();

        /// <summary>
        /// Gets the file icon from SHGetFileInfo
        /// </summary>
        /// <param name="ext">The file extension</param>
        /// <returns>The icon object</returns>
        public static Icon GetFileIconImage(string ext)
        {
            if (FileIconRepository.iconCache.ContainsKey(ext))
            {
                return FileIconRepository.iconCache[ext];
            }
            else
            {
                SHFILEINFO shinfo = new SHFILEINFO();

                string path = System.IO.Path.GetTempPath();

                string fullPath = System.IO.Path.Combine(path, Environment.TickCount.ToString() + ext);
                FileStream fs = File.Create(fullPath);
                fs.Close();
                IntPtr icoH = Win32.SHGetFileInfo(fullPath, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), Win32.SHGFIICON | Win32.SHGFISMALLICON);

                File.Delete(fullPath);
                if (icoH != IntPtr.Zero)
                {
                    Icon ico = Icon.FromHandle(shinfo.Hicon);
                    FileIconRepository.iconCache.Add(ext, ico);

                    return ico;
                }
            }

            return null;
        }

        /// <summary>
        /// Changes an icon to an image source
        /// </summary>
        /// <param name="icon">The icon object</param>
        /// <returns>The image source</returns>
        public static ImageSource ToImageSource(this Icon icon)
        {
            Bitmap bitmap = icon.ToBitmap();
            IntPtr hbitmap = bitmap.GetHbitmap();

            ImageSource wpfBitmap = Imaging.CreateBitmapSourceFromHBitmap(
                hbitmap,
                IntPtr.Zero,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());

            if (!DeleteObject(hbitmap))
            {
                throw new Win32Exception();
            }

            return wpfBitmap;
        }

        [DllImport("gdi32.dll", SetLastError = true)]
        private static extern bool DeleteObject(IntPtr hobject);

        #region ICON_FILE_API

        /// <summary>
        /// Contains information about a file object.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct SHFILEINFO
        {
            /// <summary>
            /// A handle to the icon that represents the file.
            /// </summary>
            public IntPtr Hicon;

            /// <summary>
            /// The index of the icon image within the system image list. 
            /// </summary>
            public IntPtr Iicon;

            /// <summary>
            /// An array of values that indicates the attributes of the file object.
            /// </summary>
            public uint Dwattributes;

            /// <summary>
            /// A string that contains the name of the file as it appears in the Windows Shell.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string SzdisplayName;

            /// <summary>
            /// A string that describes the type of file.
            /// </summary>
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            public string SztypeName;
        }

        /// <summary>
        /// Internal class that has the raw structures for SHGetFileInfo
        /// </summary>
        private class Win32
        {
            /// <summary>
            /// Get the icon
            /// </summary>
            public const uint SHGFIICON = 0x000000100;

            /// <summary>
            /// Get display name
            /// </summary>
            public const uint SHGFIDISPLAYNAME = 0x000000200;

            /// <summary>
            /// Get type name
            /// </summary>
            public const uint SHGFITYPENAME = 0x000000400;

            /// <summary>
            /// Get attributes
            /// </summary>
            public const uint SHGFIATTRIBUTES = 0x000000800;

            /// <summary>
            /// Get icon location
            /// </summary>
            public const uint SHGFIICONLOCATION = 0x000001000;

            /// <summary>
            /// Return exe type
            /// </summary>
            public const uint SHGFIEXETYPE = 0x000002000;

            /// <summary>
            /// Get system icon index
            /// </summary>
            public const uint SHGFISYSICONINDEX = 0x000004000;

            /// <summary>
            /// Put a link overlay on icon
            /// </summary>
            public const uint SHGFILINKOVERLAY = 0x000008000;

            /// <summary>
            /// Show icon in selected state
            /// </summary>
            public const uint SHGFISELECTED = 0x000010000;

            /// <summary>
            /// Get only specified attributes
            /// </summary>
            public const uint SHGFIATTRSPECIFIED = 0x000020000;

            /// <summary>
            /// Get large icon
            /// </summary>
            public const uint SHGFILARGEICON = 0x000000000;

            /// <summary>
            /// Get small icon
            /// </summary>
            public const uint SHGFISMALLICON = 0x000000001;

            /// <summary>
            /// Get open icon
            /// </summary>
            public const uint SHGFIOPENICON = 0x000000002;

            /// <summary>
            /// Get shell size icon
            /// </summary>
            public const uint SHGFISHELLICONSIZE = 0x000000004;

            /// <summary>
            /// pszPath is a pidl
            /// </summary>
            public const uint SHGFIPIDL = 0x000000008;

            /// <summary>
            /// Use passed dwFileAttribute
            /// </summary>
            public const uint SHGFIUSEFILEATTRIBUTES = 0x000000010;

            /// <summary>
            /// Apply the appropriate overlays
            /// </summary>
            public const uint SHGFIADDOVERLAYS = 0x000000020;

            /// <summary>
            /// Get the index of the overlay
            /// </summary>
            public const uint SHGFIOVERLAYINDEX = 0x000000040;

            /// <summary>
            /// A directory
            /// </summary>
            public const uint FILEATTRIBUTEDIRECTORY = 0x00000010;

            /// <summary>
            /// A regular file
            /// </summary>
            public const uint FILEATTRIBUTENORMAL = 0x00000080;  

            /// <summary>
            /// Retrieves information about an object in the file system, such as a file, folder, directory, or drive root.
            /// </summary>
            /// <param name="pszpath">A pointer to a null-terminated string of maximum length MAX_PATH that contains the path and file name.</param>
            /// <param name="dwfileAttributes">A combination of one or more file attribute flags.</param>
            /// <param name="psfi">The address of a SHFILEINFO structure to receive the file information.</param>
            /// <param name="cbsizeFileInfo">The size, in bytes, of the SHFILEINFO structure pointed to by the psfi parameter.</param>
            /// <param name="uflags">The flags that specify the file information to retrieve.</param>
            /// <returns>Returns a value whose meaning depends on the uFlags parameter.</returns>
            [DllImport("shell32.dll")]
            public static extern IntPtr SHGetFileInfo(string pszpath, uint dwfileAttributes, ref SHFILEINFO psfi, uint cbsizeFileInfo, uint uflags);
        }

        #endregion
    }
}